﻿using System;
using System.Globalization;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Xenta.Attributes;
using Xenta.Enums;
using Xenta.Utils;

namespace Xenta.Tasks
{
    [Dependency("b5e6c06f-1e1b-43a1-881e-10ac25d6048e",
        Lifestyle = DependencyLifestyle.Singleton)]
    public sealed class DefaultTaskScheduler : ITaskScheduler
    {
        #region Constants

        public const int SLEEP_TIME = 1000 * 30; // 30 seconds

        #endregion

        #region Fields

        private readonly IInfrastructure _infrastructure;
        private readonly string _filePath;
        private Thread _thread;

        #endregion

        #region Ctors

        public DefaultTaskScheduler(IInfrastructure infrastructure)
        {
            _infrastructure = infrastructure;
            _thread = null;
            _filePath = Path.Combine(_infrastructure.Env.DataDir, "b5e6c06f-1e1b-43a1-881e-10ac25d6048e.xml");
        }

        #endregion

        #region Methods

        public void Start()
        {
            if(_thread != null && _thread.IsAlive) 
                return;

            _thread = new Thread(ThreadProc);
            _thread.Start();
        }

        public void Stop()
        {
            if(_thread == null || !_thread.IsAlive) 
                return;

            _thread.Interrupt();
            _thread = null;
        }

        public void Dispose()
        {
            Stop();
        }

        #endregion

        #region Utilities

        private void ThreadProc()
        {
            var threads = new Dictionary<String, Thread>();

            try
            {
                while(true)
                {
                    using(var ioc = _infrastructure.DependencyResolver())
                    {
                        var dtNow = DateTime.UtcNow;
                        XElement xOldSchedule = null;
                        var xNewSchedule = new XElement("schedule");
                        var _tasks = Enumerable.Empty<ITask>();

                        if(File.Exists(_filePath))
                        {
                            try
                            {
                                xOldSchedule = XElement.Load(_filePath);
                            }
                            catch(Exception ex)
                            {
                                _infrastructure.Log.Error(ex.Message, ex);
                            }
                        }

                        try
                        {
                            _tasks = ioc.ResolveAll<ITask>();
                        }
                        catch(Exception ex)
                        {
                            _infrastructure.Log.Error(ex.Message, ex);
                        }

                        foreach(var i in _tasks)
                        {
                            var period = i.Period;
                            if(period.Equals(TimeSpan.Zero))
                                continue;

                            var attr = ReflectionUtility.Attribute<DependencyAttribute>(i.GetType());
                            string taskKey = String.Format("task_{0}", attr.ID);
                            var execTime = dtNow;

                            if(xOldSchedule != null)
                            {
                                var xTask = xOldSchedule.Element(taskKey);
                                if(xTask != null)
                                    DateTime.TryParse(xTask.Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out execTime);
                            }

                            if(dtNow.CompareTo(execTime) >= 0)
                            {
                                if(!threads.ContainsKey(taskKey) || !threads[taskKey].IsAlive)
                                {
                                    var tskThread = new Thread(i.TaskProc)
                                    {
                                        Priority = i.Priority
                                    };

                                    threads[taskKey] = tskThread;

                                    tskThread.Start();
                                }
                                execTime = dtNow.Add(period);
                            }
                            xNewSchedule.Add(new XElement(taskKey, execTime.ToString(CultureInfo.InvariantCulture)));
                        }
                        xNewSchedule.Save(_filePath);
                    }
                    Thread.Sleep(SLEEP_TIME);
                }
            }
            catch(ThreadInterruptedException)
            {
                foreach(var i in threads.Values)
                    i.Interrupt();
            }
            catch(Exception ex)
            {
                _infrastructure.Log.Fatal(ex.Message, ex);
            }
        }

        #endregion
    }
}
